
#include "GameError.h"
#include "MapScene.h"
#include "msgDefine.h"
#include "MapManager.h"
#include "BattleManager.h"
#include "CreatureManager.h"
#include "NpcManager.h"


extern RECV_MSG_MAPINFO gMsgMapInfo;
extern RECV_MSG_ENTERMAP gMsgEnterMap;
extern RECV_MSG_PLAYERENTERMAP gMsgPlayerEnterMap;
extern RECV_MSG_EXITMAP gMsgExitMap;
extern RECV_MSG_SYN_MAP_TEAM gMsgSynMapTeam;
extern RECV_MSG_TALK_NPC gMsgTalkNpc;
extern RECV_MSG_NPCENTERMAP gMsgNpcEnterMap;
extern RECV_MSG_NPC_MOVE gMsgNpcMove;

MapGridIDVector MapScene::sPathCache;

MapScene::MapScene()
:	mMapGrids( F_NULL )
{
	
}

MapScene::~MapScene()
{
	
}


fint32			MapScene::GetID()
{
	return mID;
}

fvoid			MapScene::SetID( fint32 id )
{
	mID = id;
}

fvoid			MapScene::Init( fint32 maxX , fint32 maxY )
{
	mMaxGridX = maxX;
	mMaxGridY = maxY;

	if ( mMapGrids )
	{
		assert( 0 );
	}


	mMapGrids = new MapGrid[ maxX * maxY ];

	//crtManager::crtMemcpy( mMapState , data , maxX * maxY * sizeof( fbyte ) );
}

fbool               MapScene::CheckGrid( fint32 x , fint32 y )
{
    if ( x < 0 || y < 0 )
    {
        return F_FALSE;
    }
    
    if( x >= mMaxGridX || y >= mMaxGridY )
    {
        return F_FALSE;
    }
    
    return F_TRUE;
}

MapGrid*			MapScene::GetGrid( const MapGridID& grid )
{
	return mMapGrids + GetIndex( grid );
}

fint32			MapScene::GetIndex( const MapGridID& id )
{
	if ( id.x >= mMaxGridX || id.y >= mMaxGridY ||
		id.x < 0 || id.y < 0 )
	{
		return INVALID_ID;
	}

	return id.y * mMaxGridX + id.x;
}

fvoid			MapScene::SetBlock( const MapGridID& id , fbool b /* = F_TRUE */ )
{
	mMapGrids[ GetIndex( id ) ].mIsBlock = b;
}

fbool			MapScene::IsBlock( const MapGridID& id )
{
	fint32 index = GetIndex( id );

	if ( index == INVALID_ID )
	{
		return F_TRUE;
	}

	return mMapGrids[ index ].mIsBlock;
}


fvoid			MapScene::SendMsgToAll( netMsgHead* msg , fint32 self /* = INVALID_ID */ )
{
	GamePlayerListIter iterEnd = mPlayers.end();

	for ( GamePlayerListIter iter = mPlayers.begin() ; iter != iterEnd ; ++iter )
	{
		if ( self != (*iter)->GetGUID() )
		{
			(*iter)->PackMsg( msg );
			(*iter)->SendMsg();
		}
	}
}


fvoid			MapScene::SendMsgToAllMove( netMsgHead* msg , fint32 self /* = INVALID_ID */ )
{
	GamePlayerListIter iterEnd = mPlayers.end();
    
	for ( GamePlayerListIter iter = mPlayers.begin() ; iter != iterEnd ; ++iter )
	{
        GamePlayer* player = *iter;
        fint32 guid = player->GetGUID();
        
		if ( self != (*iter)->GetGUID() )
		{
			player->SendMsgToAllEE( msg , guid );
		}
        else
        {
            player->SendMsgToAllEE( msg , guid );
            player->SendMsgToAll( msg , guid );
        }
	}
}



fvoid			MapScene::AddPlayer( GamePlayer* p )
{
	SynPlayers( p );
	SynPlayersEnter( p );

	GameTeam* team = p->GetTeam();

	if ( team )
	{
		if ( !team->IsLeader( p ) )
		{
			assert( 0 );
		}

		for ( fint32 i = 1 ; i < team->mMemberCount ; i++ )
		{
			team->mMembers[ i ]->GetMoveData().Pos = p->GetMoveData().Pos;
			SynPlayers( (GamePlayer*)team->mMembers[ i ] );
		}

		mPlayers.push_back( p );

		for ( fint32 i = 1 ; i < team->mMemberCount ; i++ )
		{
			mPlayers.push_back( (GamePlayer*)team->mMembers[ i ] );
		}
	}
	else
	{
		mPlayers.push_back( p );
	}
    
    p->UpdateSyncMove();
    
    if ( !p->mMoveData.SyncList.empty() )
    {
        SyncCreatureListIter iterEnd = p->mMoveData.SyncList.end();
        for ( SyncCreatureListIter iter = p->mMoveData.SyncList.begin() ; iter != iterEnd ; ++iter )
        {
            if ( *iter == p->GetGUID() )
            {
                continue;
            }
            
            GamePlayer* player = gCreatureManager->GetPlayer( *iter );
            
            if ( !player )
            {
                continue;
            }
            
            player->UpdateSyncMove();
        }
    }
    p->mMoveData.SyncEnter.clear();
    p->mMoveData.SyncExit.clear();
}


fvoid			MapScene::RemovePlayer( GamePlayer* p )
{
	mPlayers.remove( p );
	mMoveCreatures.remove( p );

	GameTeam* team = p->GetTeam();

	if ( team )
	{
		if ( !team->IsLeader( p ) )
		{
			assert( 0 );
		}

		for ( fint32 i = 1 ; i < team->mMemberCount ; i++ )
		{
			mPlayers.remove( (GamePlayer*)team->mMembers[ i ] );
			mMoveCreatures.remove( (GamePlayer*)team->mMembers[ i ] );
		}
	}

	// 
	SynPlayersExit( p );
}


fvoid			MapScene::SynPlayers( GamePlayer* p )
{
	gMsgEnterMap.Pos = p->mMoveData.Pos;

	gMsgMapInfo.Count = 0;
	gMsgSynMapTeam.Count = 0;
	fint32 count = 0;

	GamePlayerListIter iterEnd = mPlayers.end();

	gMsgMapInfo.size = sizeof( netMsgHead ) + sizeof( fint32 );
	for ( GamePlayerListIter iter = mPlayers.begin() ; iter != iterEnd ; ++iter )
	{
		if ( gMsgMapInfo.Count >= MAX_MAP_PLAYER )
		{
			continue;
		}

		CreatureData& data = (*iter)->GetData();
		CreatureFightData& fightData = (*iter)->GetFightData();

		gMsgMapInfo.Info[ gMsgMapInfo.Count ].GUID = data.ID;
		MEMCPY( gMsgMapInfo.Info[ gMsgMapInfo.Count ].Name , data.Name , MAX_NAME );
		gMsgMapInfo.Info[ gMsgMapInfo.Count ].Role = data.Role;
		gMsgMapInfo.Info[ gMsgMapInfo.Count ].Color = data.Color;
        gMsgMapInfo.Info[ gMsgMapInfo.Count ].RidingPet = data.RidingPet;
        gMsgMapInfo.Info[ gMsgMapInfo.Count ].WeaponType = data.WeaponType;

		gMsgMapInfo.Info[ gMsgMapInfo.Count ].Level = data.Level;
		gMsgMapInfo.Info[ gMsgMapInfo.Count ].LevelPRO = data.LevelPRO;

		gMsgMapInfo.Info[ gMsgMapInfo.Count ].HP = fightData.Base.HP;
		gMsgMapInfo.Info[ gMsgMapInfo.Count ].MAXHP = fightData.Base.MAXHP;
		gMsgMapInfo.Info[ gMsgMapInfo.Count ].MP = fightData.Base.MP;
		gMsgMapInfo.Info[ gMsgMapInfo.Count ].MAXMP = fightData.Base.MAXMP;

		gMsgMapInfo.Info[ gMsgMapInfo.Count ].Pos = (*iter)->GetMoveData().Pos;

		gMsgMapInfo.Count++;

		GameTeam* team = (*iter)->GetTeam();

		if ( team && team->IsLeader( (*iter) ) )
		{
			gMsgSynMapTeam.Data[ count ] = team->mMemberCount; count++;

			for ( fint32 i = 0 ; i < team->mMemberCount ; i++ )
			{
				gMsgSynMapTeam.Data[ count ] = team->mMembers[ i ]->GetGUID(); count++;
			}

			gMsgSynMapTeam.Count++;
		}
	}
	
	gMsgNpcEnterMap.Count = 0;
	gMsgNpcEnterMap.size = sizeof( netMsgHead ) + sizeof( fint32 );
	GameCreatureMapIter iterEnd1 = mNpc.end();
	
	for ( GameCreatureMapIter iter = mNpc.begin() ; iter != iterEnd1 ; ++iter )
	{
//		NpcCreateData& data = ((GameNpc*)(iter->second))->GetNpcCreateData();
//		gMsgNpcEnterMap.Npc[ gMsgNpcEnterMap.Count ].ID = data.ID;
//		gMsgNpcEnterMap.Npc[ gMsgNpcEnterMap.Count ].Px = data.Px;
//		gMsgNpcEnterMap.Npc[ gMsgNpcEnterMap.Count ].Py = data.Py;
//		gMsgNpcEnterMap.Npc[ gMsgNpcEnterMap.Count ].X = data.X;
//		gMsgNpcEnterMap.Npc[ gMsgNpcEnterMap.Count ].Y = data.Y;
//		gMsgNpcEnterMap.Npc[ gMsgNpcEnterMap.Count ].Texture = data.Texture;
//
//		MEMCPY( gMsgNpcEnterMap.Npc[ gMsgNpcEnterMap.Count ].Name , data.Name.c_str() , MAX_NAME );
		
		gMsgNpcEnterMap.Count++;
	}
	
	//gMsgNpcEnterMap.size += gMsgNpcEnterMap.Count * sizeof( NpcData );
	gMsgMapInfo.size += gMsgMapInfo.Count * sizeof( PlayerInfo );

	gMsgSynMapTeam.size = sizeof( netMsgHead ) + sizeof( fint32 ) + count * sizeof( fint32 );
	
	//
	p->PackMsg( &gMsgEnterMap );
	p->PackMsg( &gMsgMapInfo );
	p->PackMsg( &gMsgNpcEnterMap );
	p->PackMsg( &gMsgSynMapTeam );

}


fvoid			MapScene::SynPlayersEnter( GamePlayer* p )
{
	CreatureData& data = p->GetData();
	CreatureFightData& fightData = p->GetFightData();

	gMsgPlayerEnterMap.Info.GUID = data.ID;
	MEMCPY( gMsgPlayerEnterMap.Info.Name , data.Name , MAX_NAME );

	gMsgPlayerEnterMap.Info.Role = data.Role;
	gMsgPlayerEnterMap.Info.Color = data.Color;
    gMsgPlayerEnterMap.Info.RidingPet = data.RidingPet;
	gMsgPlayerEnterMap.Info.WeaponType = data.WeaponType;
    

	gMsgPlayerEnterMap.Info.Level = data.Level;
	gMsgPlayerEnterMap.Info.LevelPRO = data.LevelPRO;

	gMsgPlayerEnterMap.Info.HP = fightData.Base.HP;
	gMsgPlayerEnterMap.Info.MAXHP = fightData.Base.MAXHP;
	gMsgPlayerEnterMap.Info.MP = fightData.Base.MP;
	gMsgPlayerEnterMap.Info.MAXMP = fightData.Base.MAXMP;

	gMsgPlayerEnterMap.Info.Pos = p->GetMoveData().Pos;

	gMsgPlayerEnterMap.TeamCount = 0;
	
	
	// syn team,,,,

	GameTeam* team = p->GetTeam();

	if ( team )
	{
		if ( !team->IsLeader( p ) )
		{
			assert( 0 );
		}

		gMsgPlayerEnterMap.TeamCount = team->mMemberCount;
		
		for ( fint32 i = 1 ; i < gMsgPlayerEnterMap.TeamCount ; i++ )
		{
			CreatureData& data = team->mMembers[ i ]->GetData();
			CreatureFightData& fightData = team->mMembers[ i ]->GetFightData();

			gMsgPlayerEnterMap.Team[ i - 1 ].GUID = data.ID;
			MEMCPY( gMsgPlayerEnterMap.Team[ i - 1 ].Name , data.Name , MAX_NAME );

			gMsgPlayerEnterMap.Team[ i - 1 ].Role = data.Role;
			gMsgPlayerEnterMap.Team[ i - 1 ].Color = data.Color;
            gMsgPlayerEnterMap.Team[ i - 1 ].RidingPet = data.RidingPet;
            gMsgPlayerEnterMap.Team[ i - 1 ].WeaponType = data.WeaponType;
            
			gMsgPlayerEnterMap.Team[ i - 1 ].Level = data.Level;
			gMsgPlayerEnterMap.Team[ i - 1 ].LevelPRO = data.LevelPRO;

			gMsgPlayerEnterMap.Team[ i - 1 ].HP = fightData.Base.HP;
			gMsgPlayerEnterMap.Team[ i - 1 ].MAXHP = fightData.Base.MAXHP;
			gMsgPlayerEnterMap.Team[ i - 1 ].MP = fightData.Base.MP;
			gMsgPlayerEnterMap.Team[ i - 1 ].MAXMP = fightData.Base.MAXMP;

			gMsgPlayerEnterMap.Team[ i - 1 ].Pos = p->GetMoveData().Pos;
		}

		gMsgPlayerEnterMap.TeamCount--;
	}

	gMsgPlayerEnterMap.size = 8 + sizeof( PlayerInfo ) * ( gMsgPlayerEnterMap.TeamCount + 1 );

	SendMsgToAll( &gMsgPlayerEnterMap );
}


fvoid			MapScene::SynPlayersExit( GamePlayer* p )
{
	gMsgExitMap.GUID = p->GetGUID();

	SendMsgToAll( &gMsgExitMap );
}


fvoid			MapScene::Direction( GamePlayer* player , fint32 direction )
{
	CreatureMoveData& moveData = player->GetMoveData();

	if ( player->CheckState( GameCreature::GCSSTAND ) )
	{
		moveData.Direction = (SACreatureMoveDirection)direction;
	}
	else if ( player->CheckState( GameCreature::GCSMOVE ) )
	{
		moveData.IsDirectionBuffer = F_TRUE;
		moveData.DirectionBuffer = (SACreatureMoveDirection)direction;
	}

	//.Direction = direction;
	//gMsgDirection.GUID = player->GetGUID();

	//SendMsgToAll( &gMsgDirection );
}


fvoid			MapScene::AddNpc( GameNpc* n )
{
	//mNpc[ n->GetNpcCreateData().ID ] = n;
}


fvoid			MapScene::RemoveNpc( GameNpc* n )
{
	GameCreatureMapIter iter = mNpc.find( n->GetGUID() );

	if ( iter == mNpc.end() )
	{
		assert( 0 );
		return;
	}

	mNpc.erase( iter );
}


GameNpc*		MapScene::GetNpc( fint32 id )
{
	GameCreatureMapIter iter = mNpc.find( id );

	if ( iter != mNpc.end() )
	{
		return (GameNpc*)iter->second;
	}

	return F_NULL;
}


fvoid			MapScene::MoveNpc( GameNpc* n )
{
//	NpcData& data = n->GetNpcData();
//	
//	gMsgNpcMove.ID = data.ID;
//	gMsgNpcMove.Fall = n->IsFall();
//	gMsgNpcMove.X = data.X;
//	gMsgNpcMove.Y = data.Y;
//
//	SendMsgToAll( &gMsgNpcMove );
}


//////////////////////////////////////////////////////////////////////////
// move

fint32          MapScene::GetIndex( fint32 x , fint32 y )
{
    return y * mMaxGridX + x;
}
fint32          MapScene::GetXWithIndex( fint32 index )
{
    return index % mMaxGridX;
}
fint32          MapScene::GetYWithIndex( fint32 index )
{
    return index / mMaxGridX;
}

GamePlayerList* MapScene::GetPlayerLists()
{
    return &mPlayers;
}


fbool           MapScene::UncompressePath( fint32* path , fint32 count )
{
    sPathCache.clear();
    
    MapGridID grid1;
    MapGridID grid2;
    
    if ( count < 2 )
    {
        return F_FALSE;
    }
    
    fint32 index1 = path[ 0 ];
    grid1.x = GetXWithIndex( index1 );
    grid1.y = GetYWithIndex( index1 );
    
    for ( int i = 1 ; i < count ; i++ )
    {
        int index2 = path[ i ];
        
        grid2.x = GetXWithIndex( index2 );
        grid2.y = GetYWithIndex( index2 );
        
        int direct = GetDirection( grid2 , grid1 );
        
        switch ( direct )
        {
			case SACMD_EAST:
				for ( int j = 0 ; j < grid2.x - grid1.x ; j++ )
				{
                    grid1.x = grid1.x + j;
					sPathCache.push_back( grid1 );
				}
				break;
			case SACMD_SOUTH:
				for ( int j = 0 ; j < grid2.y - grid1.y ; j++ )
				{
                    grid1.y = grid1.y + j;
					sPathCache.push_back( grid1 );
				}
				break;
			case SACMD_WEST:
				for ( int j = 0 ; j < grid1.x - grid2.x ; j++ )
				{
                    grid1.x = grid1.x - j;
					sPathCache.push_back( grid1 );
				}
				break;
			case SACMD_NORTH:
				for ( int j = 0 ; j < grid1.y - grid2.y ; j++ )
				{
                    grid1.y = grid1.y - j;
					sPathCache.push_back( grid1 );
				}
				break;
			case SACMD_NORTHEAST:
				for ( int j = 0 ; j < grid2.x - grid1.x ; j++ )
				{
                    grid1.x = grid1.x + j;
                    grid1.y = grid1.y - j;
					sPathCache.push_back( grid1 );
				}
				break;
			case SACMD_NORTHWEST:
				for ( int j = 0 ; j < grid1.x - grid2.x ; j++ )
				{
					grid1.x = grid1.x - j;
                    grid1.y = grid1.y - j;
					sPathCache.push_back( grid1 );
				}
				break;
			case SACMD_SOUTHEAST:
				for ( int j = 0 ; j < grid2.x - grid1.x ; j++ )
				{
					grid1.x = grid1.x + j;
                    grid1.y = grid1.y + j;
					sPathCache.push_back( grid1 );
				}
				break;
			case SACMD_SOUTHWEST:
				for ( int j = 0 ; j < grid1.x - grid2.x ; j++ )
				{
                    grid1.x = grid1.x - j;
                    grid1.y = grid1.y + j;
					sPathCache.push_back( grid1 );
				}
				break;
			default:
				// error,
				return F_FALSE;
        }
        
        grid1.x = grid2.x;
        grid1.y = grid2.y;
        index1 = index2;
    }
    
    sPathCache.push_back( grid1 );
    
    return true;
}

fbool			MapScene::AddMove( GameCreature* c , fint32* path , fint32 count )
{
    if ( !UncompressePath( path , count ) )
    {
        c->AddDanger( GAME_DESTRUCTIVELY_DANGER );
        return F_FALSE;
    }
    
	if ( c->CheckState( GameCreature::GCSMOVE ) )
	{
		fint32 pos = c->mMoveData.IsPathIn( c->mMoveData.Pos.Grid );
		fint32 newPos = c->mMoveData.IsPathIn( sPathCache[ 0 ] );

		if ( newPos == INVALID_ID /*|| newPos == 0 */)
		{
            //FLOG0( "c->AddDanger( GAME_DESTRUCTIVELY_DANGER );" );
			//GameError::FireError( GAME_ERROR_MOVEPATH );
			//c->AddDanger( GAME_DESTRUCTIVELY_DANGER );
			//return F_FALSE;
		}

		if ( MAX_MOVE_ERROR + pos < newPos )
		{
            //FLOG0( "c->AddDanger( GDLEVEL3 );" );
			//c->AddDanger( GDLEVEL3 );
		}

		c->mMoveData.SetGrid( sPathCache[ 0 ] );
		c->mMoveData.Set( &sPathCache[ 0 ] , (fint32)sPathCache.size() );

		return F_TRUE;
	}
	else if ( c->CheckState( GameCreature::GCSSTAND ) )
	{
		if ( c->mMoveData.Pos.Grid != sPathCache[0] )
		{
			//logManager::Print( "check x%d , y%d" , c->mMoveData.Pos.Grid.x , c->mMoveData.Pos.Grid.y );
			fint32 pos = c->mMoveData.IsPathIn( sPathCache[0] );

			if ( pos == INVALID_ID || pos == 0 )
			{
				if ( pos == INVALID_ID )
				{
 					if ( c->mMoveData.Pos.Grid.distanceGrid( sPathCache[ 0 ] ) > MAX_MOVE_ERROR )
 					{
                        // server or client too fast...
                        //FLOG0( "c->AddDanger( GDLEVEL5 );" );
 						c->AddDanger( GDLEVEL5 );
 						//GamePlayer* p = (GamePlayer*)c;
						c->mMoveData.Pos.Grid = sPathCache[ 0 ];
 						//p->GetSocket()->Close();
 					}
 					else
					{
                        //FLOG0( "c->AddDanger( GDLEVEL2 );" );
						//c->AddDanger( GDLEVEL2 );
						c->mMoveData.Pos.Grid = sPathCache[ 0 ];
					}
				}
				else
				{

				}

				//return F_TRUE;
			}
			else
			{
				c->mMoveData.SetGrid( sPathCache[ 0 ] );
			}
		}

		c->mMoveData.Set( &sPathCache[ 0 ] , (fint32)sPathCache.size() );
		c->SetState( GameCreature::GCSMOVE );
		mMoveCreatures.push_back( c );

		return F_TRUE;
	}

	return F_FALSE;
}



fvoid			MapScene::Update( freal32 delta )
{
	GameCreatureListIter iterEnd = mMoveCreatures.end();

	for ( GameCreatureListIter iter = mMoveCreatures.begin() ; iter != iterEnd ; )
	{
		(*iter)->mMoveData.AddTime( delta );
		fint32 n = (*iter)->mMoveData.Move();

		GameTeam* team = (*iter)->GetTeam();

		if ( team )
		{
			team->UpdatePos();
		}

		if ( n == -1 )
		{
			++iter;
		}
		else if ( n == 1 )
		{
			MapGrid& grid = mMapGrids[ GetIndex( (*iter)->mMoveData.Pos.Grid ) ];

			if ( (*iter)->mMoveData.MovePathPos > 1 && !grid.mLinks.empty() )
			{
				(*iter)->mMoveData.EndMove();
				(*iter)->SetState( GameCreature::GCWAITCHANGEMAP );
                
				gMapManager->AddWaitPlayer( (GamePlayer*)(*iter) );
				iter = mMoveCreatures.erase( iter );
				continue;
			}

			(*iter)->mMoveData.Enemy += grid.mEnemyRand;
            
			if ( (*iter)->mMoveData.Enemy >= MAX_RANDOM_ENEMY )
			{
				(*iter)->mMoveData.Enemy = 0;
				fbool b = gBattleManager->CreatBattle( grid.mECID , (GamePlayer*)(*iter) );
				if ( b )
				{
					(*iter)->StartFight();
					iter = mMoveCreatures.erase( iter );
					continue;
				}
			}

			(*iter)->SetState( GameCreature::GCSSTAND );
			iter = mMoveCreatures.erase( iter );
		}
		else
		{
			MapGrid& grid = mMapGrids[ GetIndex( (*iter)->mMoveData.Pos.Grid ) ];

			if ( (*iter)->mMoveData.MovePathPos > 1 && !grid.mLinks.empty() )
			{
				(*iter)->mMoveData.EndMove();
				(*iter)->SetState( GameCreature::GCWAITCHANGEMAP );
                
                gMapManager->AddWaitPlayer( (GamePlayer*)(*iter) );
				iter = mMoveCreatures.erase( iter );
				continue;
			}

			(*iter)->mMoveData.Enemy += grid.mEnemyRand;

			if ( (*iter)->mMoveData.Enemy >= MAX_RANDOM_ENEMY )
			{
				(*iter)->mMoveData.Enemy = 0;
				fbool b = gBattleManager->CreatBattle( grid.mECID , (GamePlayer*)(*iter) );
				if ( b )
				{
					(*iter)->StartFight();
					iter = mMoveCreatures.erase( iter );
				}
			}
		}
		
	}
}
